package log

import (
	"fmt"
	"io"
	"os"
	"path"
	"sync"
	"time"
)

const (
	// defaultLogDir 默认日志目录
	defaultLogDir = "logs"

	// defaultLogName 默认日志文件名
	defaultLogName = "default.log"

	// defaultLogSize 默认日志分包大小
	defaultLogSize = 2 * 1024 * 1024

	// defaultLogFormat 默认日志格式
	defaultLogFormat = "%-19s %-7s [%s:%s] %s\n"

	// defaultTimeFormat 默认时间格式
	defaultTimeFormat = "2006-01-02 15:04:05"

	// defaultRenameTimeFormat 默认分包名时间格式
	defaultRenameTimeFormat = "20060102-150405"

	// minLogSize 最小日志分包大小
	minLogSize = 1 * 1024 * 1024

	// logTypeStd 标准输出
	logTypeStd logType = iota + 1

	// logTypeFile 文件输出
	logTypeFile

	// DebugTag DEBUG日志标签
	DebugTag = "[DEBUG]"

	// InfoTag INFO日志标签
	InfoTag = "[INFO]"

	// WarnTag WARN日志标签
	WarnTag = "[WARN]"

	// ErrorTag ERROR日志标签
	ErrorTag = "[ERROR]"

	// FatalTag FATAL日志标签
	FatalTag = "[FATAL]"
)

// ILoggerOption 日志设置接口
type ILoggerOption interface {
	GetLogDir() string
	GetLogName() string
	GetLogSize() int64
}

// logType 日志输出类型
type logType int

// Logger 日志对象
type Logger struct {
	sync.Once
	sync.RWMutex
	writers  map[logType]io.Writer // writer集合
	file     *os.File              // 文件句柄
	fileName string                // 日志文件名
	dir      string                // 日志存放路径
	size     int64                 // 单个日志文件的大小限制
}

// Debug 直接输出
func (l *Logger) Debug(format string, v ...interface{}) {
	l.write(formatLog(time.Now(), DebugTag, fmt.Sprintf(format, v...), 2))
}

// Info 直接输出
func (l *Logger) Info(format string, v ...interface{}) {
	l.write(formatLog(time.Now(), InfoTag, fmt.Sprintf(format, v...), 2))
}

// Warn 直接输出
func (l *Logger) Warn(format string, v ...interface{}) {
	l.write(formatLog(time.Now(), WarnTag, fmt.Sprintf(format, v...), 2))
}

// Error 直接输出
func (l *Logger) Error(format string, v ...interface{}) {
	l.write(formatLog(time.Now(), ErrorTag, fmt.Sprintf(format, v...), 2))
}

// Fatal 直接输出
func (l *Logger) Fatal(format string, v ...interface{}) {
	l.write(formatLog(time.Now(), FatalTag, fmt.Sprintf(format, v...), 2))
}

// init 初始化
func (l *Logger) init(logDir, logName string, logSize int64) error {
	l.dir = logDir
	if "" == l.dir {
		l.dir = defaultLogDir
	}

	l.fileName = logName
	if "" == l.fileName {
		l.fileName = defaultLogName
	}

	l.size = logSize
	if 0 == l.size || minLogSize > l.size {
		l.size = defaultLogSize
	}

	if nil == l.writers {
		l.writers = make(map[logType]io.Writer)
	}

	if err := makeDir(l.dir); nil != err {
		return err
	}

	fileName := path.Join(l.dir, l.fileName)
	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
	if nil != err {
		return err
	}

	l.file = file
	l.writers[logTypeStd] = os.Stdout
	l.writers[logTypeFile] = file

	return nil
}

// checkLogSize 检查文件大小
func (l *Logger) checkLogSize() {
	if nil == l.file {
		return
	}

	l.Lock()
	defer l.Unlock()

	fileInfo, err := l.file.Stat()
	if nil != err {
		panic(err)
	}

	if l.size > fileInfo.Size() {
		return
	}

	fileName := path.Join(l.dir, l.fileName)
	moveName := path.Join(l.dir, fmt.Sprintf("%s.%s.log", l.fileName, time.Now().Format(defaultRenameTimeFormat)))

	if err := l.file.Close(); nil != err {
		panic(err)
	}

	if err := os.Rename(fileName, moveName); nil != err {
		panic(err)
	}

	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
	if nil != err {
		panic(err)
	}

	l.file = file
	l.writers[logTypeFile] = file
	return
}

// write 输出
func (l *Logger) write(logs ...string) {
	l.checkLogSize()

	for _, log := range logs {
		for _, writer := range l.writers {
			fmt.Fprintf(writer, log)
		}
	}
}
